Tools.exec   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
import { lstatSafe } from '@/lib/helpers';
2
import { execSync } from 'child_process';
3
import { createHash } from 'crypto';
4
import { copyFile, copyFileSync, existsSync, lstatSync, mkdirSync, readdir, readdirSync, readFileSync, writeFileSync } from 'fs';
5
import yaml from 'js-yaml';
6
import { dirname } from 'path';
7
import { promisify } from 'util';
8
9
export type OnFileCopiedCallback = (src: string, dest: string) => void;
10
11
export class Tools {
12
    /**
13
     * This function takes a number of milliseconds and returns a promise that
14
     * resolves after that many milliseconds have passed.
15
     *
16
     * This function is used to create a delay in a program, which can be useful
17
     * for debugging, or for building a user interface with an animation.
18
     * @param {number} ms - The number of milliseconds to wait.
19
     * @returns A promise that resolves after the given number of milliseconds.
20
     */
21
    public async sleep(ms: number) {
22
        return new Promise(resolve => setTimeout(resolve, ms));
23
    }
24
25
    /**
26
     * This function returns true if the file exists, and false if it doesn't
27
     * @param {string} path - The path to the file you want to check.
28
     * @returns A boolean value.
29
     */
30
    public fileexists(path: string) {
31
        return existsSync(path);
32
    }
33
34
    /**
35
     * It reads a file and returns its contents
36
     * @param {string} path - The path to the file you want to read.
37
     * @returns The contents of the file at the given path.
38
     */
39
    public readfile(path: string) {
40
        return readFileSync(path, { encoding: 'utf8' });
41
    }
42
43
    /**
44
     * Write the given content to the given file path.
45
     * @param {string} path - The path to the file you want to write to.
46
     * @param {string} content - The content to write to the file.
47
     */
48
    public writefile(path: string, content: string) {
49
        writeFileSync(path, content, { encoding: 'utf8' });
50
    }
51
52
    /**
53
     * Copies a file from `src` to `dest`. Both `src` and `dest` should be absolute paths.
54
     * @param {string} src
55
     * @param {string} dest
56
     */
57
    public copyfile(src: string, dest: string) {
58
        if (!existsSync(dirname(dest))) {
59
            mkdirSync(dirname(dest), { recursive: true });
60
        }
61
62
        copyFileSync(src, dest);
63
64
        return dest;
65
    }
66
67
    /**
68
     * It takes a file path and returns the sha-256 hash of the file contents.
69
     * @param {string} path - The path to the file you want to hash.
70
     * @returns {string} The sha-256 hash of the file.
71
     */
72
    public hashfile(path: string) {
73
        return this.hashstring(this.readfile(path));
74
    }
75
76
    /**
77
     * It takes a string and returns the sha-256 hash.
78
     * @param {string} str - The string you want to hash.
79
     * @returns {string} The sha-256 hash of the string.
80
     */
81
    public hashstring(str: string) {
82
        return createHash('sha256').update(str)
83
            .digest('hex')
84
            .toLowerCase();
85
    }
86
87
    /**
88
     * This function returns true if the two files have the exact same contents
89
     * @param {string}
90
     * @param {string}.
91
     */
92
    public filesAreEqual(file1: string, file2: string) {
93
        if (!this.fileexists(file1) || !this.fileexists(file2)) {
94
            return false;
95
        }
96
97
        return this.hashfile(file1) === this.hashfile(file2);
98
    }
99
100
    /**
101
     * It reads a yaml file and returns the contents as an object.
102
     * @param {string} path - The path to the file you want to read.
103
     * @returns {object}
104
     */
105
    public readYaml(path: string) {
106
        return yaml.load(this.readfile(path));
107
    }
108
109
    /**
110
     * It dumps an object to yaml and saves it to the given path.
111
     * @param {string} path - The path to the file you want to write to.
112
     * @param {any} data
113
     */
114
    public writeYaml(path: string, data: any) {
115
        this.writefile(path, yaml.dump(data));
116
    }
117
118
    /**
119
     * It reads a json file and returns the parsed contents as an object.
120
     * @param {string} path - The path to the file you want to read.
121
     * @returns {object}
122
     */
123
    public readJson(path: string) {
124
        return JSON.parse(this.readfile(path));
125
    }
126
127
    /**
128
     * It writes a JSON file to the specified path.
129
     * @param {string} path - The path to the file you want to write to.
130
     * @param {any} data - The content to write to the file.
131
     */
132
    public writeJson(path: string, data: any) {
133
        this.writefile(path, JSON.stringify(data, null, 4));
134
    }
135
136
    /**
137
     * It executes a command and returns the output as a string
138
     * @param {string} command - The command to execute.
139
     * @param [silent=true] - If true, the command will not be printed to stdout.
140
     * @returns {string} The output of the command.
141
     */
142
    public exec(command: string, silent = true) {
143
        return execSync(command, { stdio: silent ? 'pipe' : 'inherit' }).toString();
144
    }
145
146
    /**
147
     * A function that recursively copies a files from one directory to another. Files that already exist
148
     * at the destination are overwritten, and directories that do not exist are created.
149
     *
150
     * Returns a list of filenames that were copied.
151
     *
152
     * @param {string} src - The source directory.
153
     * @param {string} dest - The destination directory.
154
     * @param {function|null} onCopiedCallback - A callback function that is called after each file is copied.
155
     * @returns {string[]}
156
     */
157
    public recursiveDirectoryCopy(src: string, dest: string, onCopiedCallback: OnFileCopiedCallback | null = null) {
158
        const files: string[] = [];
159
160
        const handler = (src, dest) => {
161
            readdirSync(src).forEach(file => {
162
                const srcFn = src + '/' + file;
163
                const destFn = dest + '/' + file;
164
                const stats = lstatSafe(srcFn);
165
166
                this.handleFilesForDirectoryCopy({
167
                    stats,
168
                    srcFn,
169
                    destFn,
170
                    files: new Proxy(files, {}),
171
                    onCopiedCallback,
172
                });
173
                this.handleDirectoriesForDirectoryCopy({
174
                    stats,
175
                    srcFn,
176
                    destFn,
177
                    handler,
178
                });
179
            });
180
        };
181
182
        handler(src, dest);
183
184
        return files;
185
    }
186
187
    protected handleDirectoriesForDirectoryCopy({
188
        stats, srcFn, destFn, handler 
189
    }) {
190
        if (!stats?.isDirectory()) {
191
            return;
192
        }
193
194
        if (!this.fileexists(destFn)) {
195
            mkdirSync(destFn, { recursive: true });
196
        }
197
198
        handler(srcFn, destFn);
199
    }
200
201
    protected handleFilesForDirectoryCopy({
202
        stats, srcFn, destFn, files, onCopiedCallback 
203
    }) {
204
        if (!stats?.isFile() || this.filesAreEqual(srcFn, destFn)) {
205
            return;
206
        }
207
208
        files.push(this.copyfile(srcFn, destFn));
209
210
        if (onCopiedCallback) {
211
            onCopiedCallback(srcFn, destFn);
212
        }
213
    }
214
}
215